/*
 * Copyright (C) 2012  Daniel Léonard
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package ca.forklabs.soap.axis.client;

import java.rmi.RemoteException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import org.apache.axis.client.Stub;
import ca.gc.dfo.wds.BoundaryDate;
import ca.gc.dfo.wds.BoundaryDepth;
import ca.gc.dfo.wds.BoundarySpatial;
import ca.gc.dfo.wds.Metadata;
import ca.gc.dfo.wds.ResultSet;
import ca.gc.dfo.wds.Status;

/**
 * Class {@code WDSClient} provides a framework to build clients accessing WDS
 * web services.
 *
 * @author   <a href="mailto:forklabs at gmail.com?subject=ca.forklabs.soap.axis.client.WDSClient">Daniel Léonard</a>
 */
public abstract class WDSClient {

//---------------------------
// enums
//---------------------------

   /**
    * Enum {@code SortOrder} describes the two possible sort order that can be
    * queried from the WDS web services.
    */
   public enum SortOrder {
      /** Sorts data from oldest to youngest. */
      asc,
      /** Sorts data from youngest to oldest. */
      desc,
      }


//---------------------------
// Instance variables
//---------------------------



//---------------------------
// Constructors
//---------------------------

   /**
    * Constructor.
    */
   protected WDSClient() {
   // nothing
      }


//---------------------------
// Instance methods
//---------------------------

   /**
    * Creates a new date format.
    * @return   a new date format.
    */
   protected DateFormat newDateFormatter() {
      DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$
      formatter.setTimeZone(TimeZone.getTimeZone("GMT")); //$NON-NLS-1$
      return formatter;
      }

   /**
    * Gets the {@link Stub} behind this client.
    * @return   the stub.
    */
   protected abstract Stub getBindingsStub();

   /**
    * Changes the timeout of the client.
    * @param   duration   the duration of the timeout.
    * @param   unit   the unit of the duration.
    * @see   #setTimeout(int)
    */
   public void setTimout(int duration, TimeUnit unit) {
      int millis = (int) unit.toMillis(duration);
      this.setTimeout(millis);
      }

   /**
    * Changes the timeout of the client.
    * @param   millis   the duration in milliseconds.
    */
   public void setTimeout(int millis) {
      Stub stub = this.getBindingsStub();
      stub.setTimeout(millis);
      }

   /**
    * Format the instant as a string that will be accepted by the web service.
    * @param   instant   the instant in time.
    * @return   a string representation.
    */
   @SuppressWarnings("boxing")
   protected String format(long instant) {
      DateFormat formatter = this.newDateFormatter();
      String date = formatter.format(instant);
      return date;
      }

   /**
    * Format the instants as a strings that will be accepted by the web service.
    * @param   instants   the instants in time.
    * @return   the strings representation.
    */
   @SuppressWarnings({ "boxing", "null" })
   protected String[] format(long... instants) {
      DateFormat formatter = this.newDateFormatter();

      int len = (null != instants) ? instants.length : 0;
      String[] dates = new String[len];

      for (int i = 0; i < len; i++) {
         long instant = instants[i];
         String date = formatter.format(instant);
         dates[i] = date;
         }

      return dates;
      }

   /**
    * Search the web service.
    * @param   data_type   the data type.
    * @param   min_lat   the minimum latitude of the search box.
    * @param   max_lat   the maximum latitude of the search box.
    * @param   min_long   the minimum longitude of the search box.
    * @param   max_long   the maximum longitude of the search box.
    * @param   min_depth   the depth, always {@code 0}.
    * @param   max_depth   the depth, always {@code 0}.
    * @param   min_date   the oldest value of the time box.
    * @param   max_date   the youngest value of the time box.
    * @param   start   the starting index in the list, usually but not always
    *                  {@code 1}.
    * @param   size   the number of data points to a maximum of the value
    *                 specified by meta data {@code "max_results"} (usually
    *                 600).
    * @param   metadata   indicates if meta data should be added to each data
    *                     point.
    * @param   metadataSelection   unused, always {@code ""}.
    * @param   order   the order of the data.
    * @return   the list of forecast data matching the filters.
    * @exception   RemoteException   if anything goes wrong.
    */
   public ResultSet search(String data_type,
                           double min_lat, double max_lat,
                           double min_long, double max_long,
                           double min_depth, double max_depth,
                           long min_date, long max_date,
                           int start, int size,
                           boolean metadata, String metadataSelection,
                           SortOrder order) throws RemoteException {
      String minimal_date = this.format(min_date);
      String maximal_date = this.format(max_date);
      ResultSet results = this.search(data_type,
                                      min_lat, max_lat,
                                      min_long, max_long,
                                      min_depth, max_depth,
                                      minimal_date, maximal_date,
                                      start, size, metadata, metadataSelection, order);
      return results;
      }


//---------------------------
// Abstract methods
//---------------------------

   /**
    * Gets the time interval data is available for.
    * @return   the boundary dates.
    * @exception   RemoteException   if anything goes wrong.
    */
   public abstract BoundaryDate getBoundaryDate() throws RemoteException;

   /**
    * Gets the depth interval data is available for.
    * @return   the boundary depths.
    * @exception   RemoteException   if anything goes wrong.
    */
   public abstract BoundaryDepth getBoundaryDepth() throws RemoteException;

   /**
    * Gets the spatial interval data is available for.
    * @return   the boundary coordinates.
    * @exception   RemoteException   if anything goes wrong.
    */
   public abstract BoundarySpatial getBoundarySpatial() throws RemoteException;

   /**
    * Gets the list of data types and their description.
    * @return   the list of data types and their descriptions.
    * @exception   RemoteException   if anything goes wrong.
    */
   public abstract Metadata[] getDataInfo() throws RemoteException;

   /**
    * Gets the description of the web service.
    * @return   the description.
    * @exception   RemoteException   if anything goes wrong.
    */
   public abstract String getInfo() throws RemoteException;

   /**
    * Gets meta data about the observations service.
    * @return   the list of meta data and their value.
    * @exception   RemoteException   if anything goes wrong.
    */
   public abstract Metadata[] getMetadata() throws RemoteException;

   /**
    * Gets the description of the various meta data.
    * @return   the list of meta data and their description.
    * @exception   RemoteException   if anything goes wrong.
    */
   public abstract Metadata[] getMetadataInfo() throws RemoteException;

   /**
    * Gets the name of the observations service.
    * @return   the name.
    * @exception   RemoteException   if anything goes wrong.
    */
   public abstract String getName() throws RemoteException;

   /**
    * Gets the status of the service.
    * @return   the status.
    * @throws   RemoteException   if anything goes wrong.
    */
   public abstract Status getStatus() throws RemoteException;

   /**
    * Gets the version of the observations service.
    * @return   the version.
    * @exception   RemoteException   if anything goes wrong.
    */
   public abstract String getVersion() throws RemoteException;

   /**
    * Search the web service.
    * @param   data_type   the data type.
    * @param   min_lat   the minimum latitude of the search box.
    * @param   max_lat   the maximum latitude of the search box.
    * @param   min_long   the minimum longitude of the search box.
    * @param   max_long   the maximum longitude of the search box.
    * @param   min_depth   the depth, always {@code 0}.
    * @param   max_depth   the depth, always {@code 0}.
    * @param   min_date   the oldest value of the time box.
    * @param   max_date   the youngest value of the time box.
    * @param   start   the starting index in the list, usually but not always
    *                  {@code 1}.
    * @param   size   the number of data points to a maximum of the value
    *                 specified by meta data {@code "max_results"} (usually
    *                 600).
    * @param   metadata   indicates if meta data should be added to each data
    *                     point.
    * @param   metadataSelection   unused, always {@code ""}.
    * @param   order   the order of the data.
    * @return   the list of forecast data matching the filters.
    * @exception   RemoteException   if anything goes wrong.
    */
   public abstract ResultSet search(String data_type,
                                    double min_lat, double max_lat,
                                    double min_long, double max_long,
                                    double min_depth, double max_depth,
                                    String min_date, String max_date,
                                    int start, int size,
                                    boolean metadata, String metadataSelection,
                                    SortOrder order) throws RemoteException;

   }
